package com.rem.chat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.rem.chat.common.config.AppConfig;
import com.rem.chat.common.constants.Constants;
import com.rem.chat.common.errcode.ServiceErrorCodeConstants;
import com.rem.chat.common.exception.ServiceException;
import com.rem.chat.common.utils.BeanTransUtil;
import com.rem.chat.common.utils.RedisComponent;
import com.rem.chat.common.utils.StringUtil;
import com.rem.chat.mapper.ChatSessionUserMapper;
import com.rem.chat.mapper.UserContactMapper;
import com.rem.chat.mapper.UserInfoMapper;
import com.rem.chat.pojo.dataobject.ChatSessionUserDO;
import com.rem.chat.pojo.dataobject.UserContactDO;
import com.rem.chat.pojo.dataobject.UserInfoDO;
import com.rem.chat.pojo.request.UserInfoSaveRequest;
import com.rem.chat.pojo.request.UserModifyPasswordRequest;
import com.rem.chat.pojo.response.GetUserInfoResponse;
import com.rem.chat.pojo.response.SearchUserInfoResponse;
import com.rem.chat.service.UserService;
import com.rem.chat.service.dto.MessageSendDTO;
import com.rem.chat.service.dto.UserLoginDTO;
import com.rem.chat.service.enums.MessageTypeEnum;
import com.rem.chat.service.enums.UserContactStatusEnum;
import com.rem.chat.service.enums.UserContactTypeEnum;
import com.rem.chat.websocket.MessageHandler;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * @author: Themberfue
 * @date: 2025/6/8 21:07
 * @description:
 */
@Service
public class UserServiceImpl implements UserService {

    @Resource
    UserInfoMapper userInfoMapper;

    @Resource
    UserContactMapper userContactMapper;

    @Resource
    ChatSessionUserMapper chatSessionUserMapper;

    @Resource
    MessageHandler messageHandler;

    @Resource
    AppConfig appConfig;

    @Resource
    private RedisComponent redisComponent;

    @Override
    public GetUserInfoResponse getUserInfo(String userId) {
        UserInfoDO userInfoDO = userInfoMapper.selectById(userId);
        return BeanTransUtil.transToGetUserInfoResponse(userInfoDO);
    }

    @Override
    public SearchUserInfoResponse searchUserInfo(String userId, String contactId) {
        // Step 1. 检查用户和被搜用户的关系
        QueryWrapper<UserContactDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(UserContactDO::getUserId, userId)
                .eq(UserContactDO::getContactId, contactId);
        UserContactDO userContactDO = userContactMapper.selectOne(queryWrapper);
        if (null == userContactDO ||
                !ArrayUtils.contains(new Integer[]{UserContactStatusEnum.FRIEND.getStatus(),
                                UserContactStatusEnum.DEL_BE.getStatus(), UserContactStatusEnum.BLACKLIST_BE.getStatus()},
                    userContactDO.getStatus())) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_CONTACT_STATUS_WRONG);
        }

        // Step 2. 获取被搜用户详细信息
        UserInfoDO userInfoDO = userInfoMapper.selectById(contactId);
        if (null == userInfoDO) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_IS_NOT_EXISTS);
        }

        SearchUserInfoResponse infoResponse = new SearchUserInfoResponse();
        BeanUtils.copyProperties(userInfoDO, infoResponse);

        return infoResponse;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeContact(String userId, String contactId, UserContactStatusEnum statusEnum) {
        // Step 1. 检查用户和被除用户的关系
        QueryWrapper<UserContactDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(UserContactDO::getUserId, userId)
                .eq(UserContactDO::getContactId, contactId);
        UserContactDO userContactDO = userContactMapper.selectOne(queryWrapper);
        if (null == userContactDO) {
            throw new ServiceException(ServiceErrorCodeConstants.NOT_USER_OPERATE);
        }

        if (!userContactDO.getStatus()
                .equals(UserContactStatusEnum.FRIEND.getStatus())) {
            throw new ServiceException(ServiceErrorCodeConstants.CONTACT_TYPE_WRONG);
        }

        // Step 2. 将用户对被除用户的关系设为 statusEnum 状态
        userContactDO.setStatus(statusEnum.getStatus());
        UpdateWrapper<UserContactDO> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda()
                .eq(UserContactDO::getUserId, userId)
                .eq(UserContactDO::getContactId, contactId);
        userContactMapper.update(userContactDO, updateWrapper);

        // Step 3. 检查是拉黑还是删除
        userContactDO.setUserId(contactId);
        userContactDO.setContactId(userId);
        if (statusEnum.equals(UserContactStatusEnum.DEL)) { // ? 删除
            // Step 3.1 将被除用户对用户的关系设为 被删除 状态
            userContactDO.setStatus(UserContactStatusEnum.DEL_BE.getStatus());
        } else { // ? 拉黑
            // Step 3.2 将被除用户对用户的关系设为 被拉黑 状态
            userContactDO.setStatus(UserContactStatusEnum.BLACKLIST_BE.getStatus());
        }
        updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda()
                .eq(UserContactDO::getUserId, contactId)
                .eq(UserContactDO::getContactId, userId);
        userContactMapper.update(userContactDO, updateWrapper);

        // Step 4. 从缓存中删除
        redisComponent.removeUserContact(userId, contactId);
        redisComponent.removeUserContact(contactId, userId);
        return true;
    }

    @Override
    public GetUserInfoResponse saveUserInfo(UserInfoSaveRequest request, MultipartFile avatarFile, MultipartFile avatarCover) throws IOException {
        // Step 1. 校验图片是否存在
        if (null == avatarFile || null == avatarCover) {
            throw new ServiceException(ServiceErrorCodeConstants.GROUP_AVATAR_NOT_EXISTS);
        }

        // Step 2. 将图像存至服务器
        File targetFileFolder = new File(appConfig.getProjectFolder() +
                Constants.FILE_FOLDER_AVATAR);
        if (!targetFileFolder.exists()) {
            boolean isMkdir = targetFileFolder.mkdirs();
            if (!isMkdir) return null;
        }
        String avatarFilePath = targetFileFolder.getPath() + "/" + request.getUserId() + Constants.IMAGE_SUFFIX;
        String avatarCoverPath = targetFileFolder.getPath() + "/" + request.getUserId() + Constants.COVER_IMAGE_SUFFIX;
        avatarFile.transferTo(new File(avatarFilePath));
        avatarCover.transferTo(new File(avatarCoverPath));

        UserInfoDO userInfo = userInfoMapper.selectById(request.getUserId());
        if (null == userInfo) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_IS_NOT_EXISTS);
        }
        // Step 3. 更新用户信息
        UserInfoDO userInfoDO = UserInfoDO.builder()
                .userId(request.getUserId())
                .nickName(request.getNickName())
                .joinType(request.getJoinType())
                .gender(request.getGender())
                .personalSignature(request.getPersonalSignature())
                .areaName(request.getAreaName())
                .areaCode(request.getAreaCode())
                .build();
        userInfoMapper.updateById(userInfoDO);

        // Step 4. 更新会话中的昵称
        String beforeNickName = userInfo.getNickName(), updateNickName = userInfoDO.getNickName();
        if (!beforeNickName.equals(updateNickName)) {
            UpdateWrapper<ChatSessionUserDO> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda()
                    .eq(ChatSessionUserDO::getContactId, userInfo.getUserId())
                    .set(ChatSessionUserDO::getContactName, updateNickName);
            chatSessionUserMapper.update(updateWrapper);

            // Step 5. 更新缓存中的昵称
            UserLoginDTO userLoginDTO = redisComponent.getUserInfoByUserId(userInfo.getUserId());
            userLoginDTO.setNickName(updateNickName);
            redisComponent.saveTokenUserInfo(userLoginDTO);

            // Step 6. 发送更新消息
            List<String> contactList = redisComponent.getUserContactList(userLoginDTO.getUserId());
            for (String contactId: contactList) {
                MessageSendDTO<Object> messageSendDTO = MessageSendDTO.builder()
                        .contactType(UserContactTypeEnum.USER.getType())
                        .contactId(contactId)
                        .sendUserId(userInfo.getUserId())
                        .sendUserNickName(updateNickName)
                        .messageType(MessageTypeEnum.CONTACT_NAME_UPDATE.getType())
                        .extendData(updateNickName)
                        .build();
                messageHandler.sendMessage(messageSendDTO);
            }
        }

        return BeanTransUtil.transToGetUserInfoResponse(
                userInfoMapper.selectById(userInfoDO.getUserId()));
    }

    @Override
    public Boolean modifyPassword(String userId, UserModifyPasswordRequest param) {
        // Step 1. 校验密码和确认密码是否相同
        if (!param.getNewPassword().equals(param.getConfirmPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.CONFIRM_PASSWORD_WRONG);
        }

        // Step 2. 校验用户是否存在
        UserInfoDO userInfoDO = userInfoMapper.selectById(userId);
        if (null == userInfoDO) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_IS_NOT_EXISTS);
        }

        // Step 3. 校验旧密码是否正确
        String oldPasswordEncrypt = userInfoDO.getPassword();
        System.out.println(StringUtil.encodeSHA256(param.getOldPassword()));
        if (!oldPasswordEncrypt.equals(
                StringUtil.encodeSHA256(param.getOldPassword()))) {
            throw new ServiceException(ServiceErrorCodeConstants.OLD_PASSWORD_IS_WRONG);
        }

        // Step 4. 校验旧密码和新密码是否相同
        oldPasswordEncrypt = StringUtil.encodeSHA256(param.getOldPassword());
        String newPasswordEncrypt = StringUtil.encodeSHA256(param.getNewPassword());
        if (oldPasswordEncrypt.equals(newPasswordEncrypt)) {
            throw new ServiceException(ServiceErrorCodeConstants.OLD_NEW_PASSWORD_SAME);
        }

        // Step 5. 更新用户密码
        userInfoDO.setPassword(newPasswordEncrypt);
        userInfoMapper.updateById(userInfoDO);

        return true;
    }
}
